package gang.org.springframework.boot.web.servlet;

import gang.org.springframework.boot.autoconfigure.web.servlet.GangServletRegistrationBean;
import gang.org.springframework.framework.aop.scope.GangScopedProxyUtils;
import gang.org.springframework.framework.core.annotation.GangAnnotationAwareOrderComparator;
import gang.org.springframework.framework.factory.GangListableBeanFactory;
import gang.org.springframework.framework.util.GangLinkedMultiValueMap;
import gang.org.springframework.framework.util.GangMultiValueMap;

import javax.servlet.Servlet;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Author：gang.chen
 * @Date：2023/10/12 11:39
 */
public class GangServletContextInitializerBeans extends AbstractCollection<GangServletContextInitializer> {

    private List<GangServletContextInitializer> sortedList;
    
    public final GangMultiValueMap<Class<?>, GangServletContextInitializer> initializers;
    
    public final List<Class<? extends GangServletContextInitializer>> initializerTypes;

    public GangServletContextInitializerBeans(GangListableBeanFactory beanFactory, Class<? extends GangServletContextInitializer>... initializerTypes)
    {
        
        this.initializers = new GangLinkedMultiValueMap<>();
        
        this.initializerTypes = initializerTypes.length != 0 ? Arrays.asList(initializerTypes) : Collections.singletonList(GangServletContextInitializer.class);
        
        addServletContextInitializerBeans(beanFactory);

        List<GangServletContextInitializer> sortedInitializers = (List<GangServletContextInitializer>) this.initializers.values().stream().flatMap(value -> value.stream().sorted(GangAnnotationAwareOrderComparator.INSTANCE)).collect(Collectors.toList());

        this.sortedList = Collections.unmodifiableList(sortedInitializers);


    }

    private void addServletContextInitializerBeans(GangListableBeanFactory beanFactory) {

        for (Class<? extends GangServletContextInitializer> initializerType : this.initializerTypes) {

            for (Map.Entry<String, ? extends GangServletContextInitializer> initializerBean : getOrderedBeansOfType(beanFactory, initializerType)) {

                addServletContextInitializerBean(initializerBean.getKey(), initializerBean.getValue(), beanFactory);
            }

        }
    }

    private void addServletContextInitializerBean(String beanName, GangServletContextInitializer initializer, GangListableBeanFactory beanFactory){

        if (initializer instanceof GangServletRegistrationBean) {

            Servlet source = ((GangServletRegistrationBean) initializer).getServlet();
            addServletContextInitializerBean(Servlet.class, beanName, initializer, beanFactory, source);
        }

        //else if TODO

        else {

            //TODO
        }
    }

    private void addServletContextInitializerBean(Class<?> type, String beanName, GangServletContextInitializer initializer, GangListableBeanFactory beanFactory, Object source){

        this.initializers.add(type, initializer);

        //TODO
    }



    private <T> List<Map.Entry<String, T>> getOrderedBeansOfType(GangListableBeanFactory beanFactory, Class<T> type) {
        return getOrderedBeansOfType(beanFactory, type, Collections.emptySet());
    }

    private <T> List<Map.Entry<String,T>> getOrderedBeansOfType(GangListableBeanFactory beanFactory, Class<T> type, Set<?> excludes) {

        String[] names = beanFactory.getBeanNamesForType(type, true, false);

        Map<String, T> map = new LinkedHashMap<>();

        for (String name : names) {

            if (!excludes.contains(name) && !GangScopedProxyUtils.isScopedTarget(name)) {

                T bean = beanFactory.getBean(name, type);

                if (!excludes.contains(bean)) {
                    map.put(name, bean);
                }
            }
        }

        List<Map.Entry<String, T>> beans = new ArrayList<>(map.entrySet());

        //beans.sort TODO
        return beans;
    }

    @Override
    public Iterator<GangServletContextInitializer> iterator() {
        //TODO
        return this.sortedList.iterator();
    }

    @Override
    public int size() {
        //TODO
        return this.sortedList.size();
    }
}
